#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/module.h>
#include "ads7870-spi.h"

#define PSOC_MAJOR       64
#define PSOC_MINOR        0
#define MAXLEN             512
#define NBR_ADC_CH           8

#define MODULE_DEBUG 0
#define USECDEV 0

static struct cdev psocDev;
struct file_operations psoc_Fops;
static int devno;

static int __init psoc_cdrv_init(void)
{
  int err; 
  
  printk("psoc driver initializing\n");  

  /* Register Char Device */
  cdev_init(&psocDev, &psoc_Fops);
	/* NEXT VERSION
	 * Leave out the following two lines
	 * LDD3 p.56
	 */
  psocDev.owner = THIS_MODULE;
  psocDev.ops   = &psoc_Fops;
  devno           = MKDEV(PSOC_MAJOR, PSOC_MINOR);
  err = register_chrdev_region(devno, NBR_ADC_CH, "PSoC5");
  err = cdev_add(&psocDev, devno, NBR_ADC_CH);
  if (err) {
    printk (KERN_ALERT "Error %d adding PSOC device\n", err);
    return err;
  }

  err = psoc_spi_init();
  
  return err;
}

static void __exit psoc_cdrv_exit(void)
{
  int err;
  printk("psoc driver Exit\n");
  err = psoc_spi_exit();
  unregister_chrdev_region(devno, NBR_ADC_CH);
  cdev_del(&psocDev);
}


int psoc_cdrv_open(struct inode *inode, struct file *filep)
{
    int major, minor;

    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);

    printk("Opening PSOC Device [major], [minor]: %i, %i\n", major, minor);

    if (minor > NBR_ADC_CH-1)
    {
      printk("Minor no out of range (0-%i): %i\n", NBR_ADC_CH, minor);
        return -ENODEV;
    }

    if (!try_module_get(psoc_Fops.owner)) // Get Module
	return -ENODEV;

    return 0;
}

int psoc_cdrv_release(struct inode *inode, struct file *filep)
{
    int minor, major;
    
    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);

    printk("Closing PSOC Device [major], [minor]: %i, %i\n", major, minor);

    if (minor > NBR_ADC_CH-1)
      return -ENODEV;
    
    module_put(psoc_Fops.owner); // Release Module
    
    return 0;
}



ssize_t psoc_cdrv_write(struct file *filep, const char __user *ubuf, 
		      size_t count, loff_t *f_pos)
{
    int minor, len, value;
    char kbuf[MAXLEN];    
    
    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    if (minor != PSOC_MINOR) {
        printk(KERN_ALERT "psoc Write to wrong Minor No:%i \n", minor);
        return 0; }
    printk(KERN_ALERT "Writing to psoc [Minor] %i \n", minor);
    
    len = count < MAXLEN ? count : MAXLEN;
    if(copy_from_user(kbuf, ubuf, len))
        return -EFAULT;
	
    kbuf[len] = '\0';   // Pad null termination to string

    if(MODULE_DEBUG)
      printk("string from user: %s\n", kbuf);
    sscanf(kbuf,"%i", &value);
    if(MODULE_DEBUG)
      printk("value %i\n", value);

    return count;
}

ssize_t psoc_cdrv_read(struct file *filep, char __user *ubuf, 
                  size_t count, loff_t *f_pos)
{
  int minor;
  char resultBuf[40];
  struct spi_result result;
    
    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    if (minor > NBR_ADC_CH-1) {
      printk(KERN_ALERT "psoc read from wrong Minor No:%i \n", minor);
      return 0; }
    if(MODULE_DEBUG)
      printk(KERN_ALERT "Reading from psoc [Minor] %i \n", minor);

    result = psoc_read();

    snprintf (resultBuf, sizeof(resultBuf), "%u,%u,%u,%u\n",
	      result.fejl,result.temperatur, result.batteri, result.stroem);
    if(copy_to_user(ubuf, resultBuf, sizeof(resultBuf)))
      return -EFAULT;
    return count;
}

struct file_operations psoc_Fops = 
{
    .owner   = THIS_MODULE,
    .open    = psoc_cdrv_open,
    .release = psoc_cdrv_release,
    .write   = psoc_cdrv_write,
    .read    = psoc_cdrv_read,
};

module_init(psoc_cdrv_init);
module_exit(psoc_cdrv_exit);

MODULE_AUTHOR("Troels Dalsgaard Hoffmeyer");
MODULE_LICENSE("GPL");

